﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.UI.WebControls;
using ICSharpCode.SharpZipLib.Core;
using ICSharpCode.SharpZipLib.Zip;
using Vega.USiteBuilder;
using umbraco.cms.businesslogic.datatype;
using umbraco.cms.businesslogic.template;
using umbraco.cms.businesslogic.web;

namespace kelvinDigital.USiteBuilderAdmin
{

    public partial class usercontrols_ExportCode : System.Web.UI.UserControl
    {
        private const string code = @"
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Vega.USiteBuilder;
{7}
{{
    [DocumentType(Name=""{0}"", IconUrl=""{1}"", 
        Alias=""{8}"",
        AllowedTemplates= new string[] {{{2}}},
        AllowedChildNodeTypes = new Type[] {{{3}}})]
    public partial class {4} : {5}
    {{
        public {4} (int nodeId) : base(nodeId){{}}
        public {4} () {{}}
        {6}
    }}
}}";

        private string property = @"
        [DocumentTypeProperty(UmbracoPropertyType.{5}, {6} Name = ""{0}"", Description = ""{1}"", Tab = ""{2}"", Mandatory = {3} )]    
        public {7} {4} {{ get; set; }}
";

        private const string dataTypeCode = @"
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using umbraco.cms.businesslogic.datatype;
    using Vega.USiteBuilder;
    {6}
    {{
        [DataType(Name = ""{0}"", UniqueId = ""{1}"", RenderControlGuid = ""{2}"", DatabaseDataType = {3})]
        public partial class {4} : DataTypeBase
        {{
            public override DataTypePrevalue[] Prevalues
            {{
                get {{ return new DataTypePrevalue[] {{ {5} }}; }}
            }}
        }}
    }}";

        private const string templateCode = @"

    using System.Web.UI;
    using Vega.USiteBuilder;

    {0}
    {{
        public partial class {1} : TemplateBase{2}
        {{
        }}
    }}
";

        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);

            txtDataTypesNamespace.Enabled = cbxExportDataTypes.Checked;
            txtDocTypesNamespace.Enabled = cbxExportDocTypes.Checked;
            txtTemplatesNamespace.Enabled = cbxExportTemplates.Checked;
        }

        protected void preview_Click(object sender, EventArgs e)
        {
            StringBuilder sb = new StringBuilder();

            if (cbxExportTemplates.Checked)
            {
                Template.GetAllAsList().ForEach(template => sb.AppendLine(ExportTemplate(template)));
            }
            if (cbxExportDataTypes.Checked)
            {
                DataTypeDefinition.GetAll().ToList().ForEach(dataType => sb.AppendLine(ExportDataType(dataType)));
            }
            if (cbxExportDocTypes.Checked)
            {
                DocumentType.GetAllAsList().ForEach(docType => sb.AppendLine(ExportDocType(docType.Id)));
            }

            litCode.Text = Server.HtmlEncode(sb.ToString());
            activateTab.Visible = true;
        }

        protected void save_Click(object sender, EventArgs e)
        {
            Response.ContentType = "application/octet-stream";
            Response.AppendHeader("content-disposition", "attachment; filename=\"Exported uSiteBuilder Classes.zip\"");
            Response.CacheControl = "Private";
            Response.Cache.SetExpires(DateTime.Now.AddMinutes(3));

            var zipStream = new ZipOutputStream(Response.OutputStream);
            zipStream.SetLevel(3);
            if (cbxExportDocTypes.Checked)
            {
                DocumentType.GetAllAsList().ForEach(docType =>
                {
                    string docTypeCode = ExportDocType(docType.Id);
                    var memoryStream = new MemoryStream();

                    using (TextWriter tw = new StreamWriter(memoryStream))
                    {
                        tw.Write(docTypeCode);
                        tw.Flush();

                        ZipEntry newEntry =
                            new ZipEntry("DocumentTypes/" + CleanName(docType.Alias) +
                                         ".cs");
                        newEntry.DateTime = DateTime.Now;
                        zipStream.PutNextEntry(newEntry);
                        memoryStream.Position = 0;

                        StreamUtils.Copy(memoryStream, zipStream, new byte[4096]);

                        zipStream.CloseEntry();
                    }
                });
            }

            if (cbxExportDataTypes.Checked)
            {
                DataTypeDefinition.GetAll().ToList().ForEach(dataType =>
                {
                    string dataTypeCode = ExportDataType(dataType);
                    var memoryStream = new MemoryStream();

                    using (
                        TextWriter tw = new StreamWriter(memoryStream))
                    {
                        tw.Write(dataTypeCode);
                        tw.Flush();

                        ZipEntry newEntry =
                            new ZipEntry("DataTypes/" +
                                         CleanName(dataType.Text) +
                                         ".cs");
                        newEntry.DateTime = DateTime.Now;
                        zipStream.PutNextEntry(newEntry);
                        memoryStream.Position = 0;

                        StreamUtils.Copy(memoryStream, zipStream,
                                         new byte[4096]);

                        zipStream.CloseEntry();
                    }
                });
            }

            if (cbxExportTemplates.Checked)
            {
                Template.GetAllAsList().ForEach(template =>
                {
                    string exportedTemplate = ExportTemplate(template);
                    var memoryStream = new MemoryStream();

                    using (TextWriter tw = new StreamWriter(memoryStream))
                    {
                        tw.Write(exportedTemplate);
                        tw.Flush();

                        ZipEntry newEntry =
                            new ZipEntry("Templates/" + CleanName(template.Alias) +
                                         ".master.cs");
                        newEntry.DateTime = DateTime.Now;
                        zipStream.PutNextEntry(newEntry);
                        memoryStream.Position = 0;

                        StreamUtils.Copy(memoryStream, zipStream, new byte[4096]);

                        zipStream.CloseEntry();
                    }

                    var memoryStream2 = new MemoryStream();

                    using (TextWriter tw = new StreamWriter(memoryStream2))
                    {
                        tw.Write("<%@ Master Language=\"C#\" AutoEventWireup=\"true\" CodeBehind=\"" + CleanName(template.Alias) + ".master.cs\" Inherits=\"" + txtTemplatesNamespace.Text +"." + CleanName(template.Alias) + "\" %>");

                        string design = template.Design;
                        int startPos = design.IndexOf(">");
                        tw.Write(design.Substring(startPos + 1));
                        tw.Flush();

                        ZipEntry newEntry =
                            new ZipEntry("Templates/" + CleanName(template.Alias) +
                                         ".master");
                        newEntry.DateTime = DateTime.Now;
                        zipStream.PutNextEntry(newEntry);
                        memoryStream2.Position = 0;

                        StreamUtils.Copy(memoryStream2, zipStream, new byte[4096]);

                        zipStream.CloseEntry();
                    }
                });
            }

            zipStream.IsStreamOwner = false;
            zipStream.Close();
            Response.End();
        }


        // these are private readonlys as const can't be Guids
        private readonly Guid DATATYPE_YESNO_GUID = new Guid("38b352c1-e9f8-4fd8-9324-9a2eab06d97a");
        private readonly Guid DATATYPE_TINYMCE_GUID = new Guid("5e9b75ae-face-41c8-b47e-5f4b0fd82f83");
        private readonly Guid DATATYPE_DATETIMEPICKER_GUID = new Guid("b6fb1622-afa5-4bbf-a3cc-d9672a442222");
        private readonly Guid DATATYPE_DATEPICKER_GUID = new Guid("23e93522-3200-44e2-9f29-e61a6fcbb79a");

        private string GetPropertyType (Guid datatype)
        {
            if(datatype == DATATYPE_YESNO_GUID)
            {
                return "bool";
            }
            if(datatype == DATATYPE_TINYMCE_GUID)
            {
                return "HtmlString";
            }
            if(datatype == DATATYPE_DATETIMEPICKER_GUID)
            {
                return "DateTime";
            }
            if(datatype == DATATYPE_DATEPICKER_GUID)
            {
                return "DateTime";
            }

            return "string";
        }

        private string CleanName(string name)
        {
            //Compliant with item 2.4.2 of the C# specification
            System.Text.RegularExpressions.Regex regex =
                new System.Text.RegularExpressions.Regex(
                    @"[^\p{Ll}\p{Lu}\p{Lt}\p{Lo}\p{Nd}\p{Nl}\p{Mn}\p{Mc}\p{Cf}\p{Pc}\p{Lm}]");
            string ret = regex.Replace(name, ""); //The identifier must start with a character 
		
			ret = ret.Substring(0, 1).ToUpper() + ret.Substring(1, ret.Length - 1);
		
			if (!char.IsLetter(ret, 0))
            {
                ret = string.Concat("_", ret);
            }

            return ret;
        }


        private string ExportTemplate(Template template)
        {
            string docTypeClass = "";

            List<DocumentType> documentTypes = DocumentType.GetAllAsList();
            if (documentTypes.Count(docType => docType.allowedTemplates.Any(tmpl => tmpl.Alias == template.Alias)) == 1)
            {
                var defaultDocumentType =
                    documentTypes.Single(docType => docType.allowedTemplates.Any(tmpl => tmpl.Alias == template.Alias));
                docTypeClass = string.IsNullOrEmpty(txtDocTypesNamespace.Text)
                                   ? string.Format("<{0}>", CleanName(defaultDocumentType.Alias))
                                   : string.Format("<{0}.{1}>", txtDocTypesNamespace.Text,  CleanName(defaultDocumentType.Alias));
            }

            return string.Format(templateCode,
                                 !string.IsNullOrEmpty(txtTemplatesNamespace.Text)
                                     ? "namespace " + txtTemplatesNamespace.Text
                                     : "",
                                 CleanName(template.Alias.Replace(" ", "")),
                                 docTypeClass);
        }

        private string ExportDataType(DataTypeDefinition dataType)
        {
            if (dataType.DataType == null)
            {
                return "";
            }

            var defaultData = dataType.DataType.Data as DefaultData;

            string dbType = "DBTypes.Nvarchar";

            if (defaultData != null)
            {
                dbType = "DBTypes." + defaultData.DatabaseType;
            }

            string preValues = "";
            var settingsStorage = new DataEditorSettingsStorage();
            var existingSettings = settingsStorage.GetSettings(dataType.Id);

            if (existingSettings.Any())
            {
                preValues = string.Join("," + Environment.NewLine,
                                        existingSettings.Select(
                                            setting =>
                                            string.Format("new DataTypePrevalue(\"{0}\", \"{1}\")", setting.Key,
                                                          setting.Value.Replace("\"", "\\\""))));
            }

            return string.Format(dataTypeCode, dataType.Text.Replace("\"", "\\\""), dataType.UniqueId.ToString(),
                                 dataType.DataType.Id.ToString(), dbType, CleanName(dataType.Text), preValues,
                                 !string.IsNullOrEmpty(txtDataTypesNamespace.Text)
                                     ? "namespace " + txtDataTypesNamespace.Text
                                     : "");
        }

        private string ExportDocType(int id)
        {
            DocumentType docType = new DocumentType(id);

            string templates = String.Join(", ",
                                           docType.allowedTemplates.Select(template => "\"" + template.Alias + "\""));
            string childNodes = String.Join(", ",
                                            docType.AllowedChildContentTypeIDs.Select(
                                                contentTypeId => "typeof(" + CleanName(new DocumentType(contentTypeId).Alias) + ")"));

            string properties = String.Join(string.Empty,
                                            docType.getVirtualTabs.SelectMany(
                                                tab =>
                                                tab.GetPropertyTypes(id, false).Select(
                                                    prop =>
                                                    String.Format(property, prop.Name.Replace("\"", "\\\""), prop.Description.Replace(Environment.NewLine, "").Replace("\"", "\\\""),
                                                                  (prop.TabId == 0
                                                                       ? "Properties"
                                                                       : umbraco.cms.businesslogic.ContentType.Tab.
                                                                             GetTab(prop.TabId).Caption),
                                                                  prop.Mandatory.ToString().ToLower(), CleanName(prop.Alias),
                                                                  Enum.IsDefined(typeof(UmbracoPropertyType),
                                                                                 prop.DataTypeDefinition.Id)
                                                                      ? ((UmbracoPropertyType)
                                                                         prop.DataTypeDefinition.Id).ToString()
                                                                      : "Other",
                                                                  Enum.IsDefined(typeof(UmbracoPropertyType),
                                                                                 prop.DataTypeDefinition.Id)
                                                                      ? ""
                                                                      : "OtherTypeName=\"" +
                                                                        prop.DataTypeDefinition.Text.Replace("\"", "\\\"") + "\", ",
                                                                        GetPropertyType(umbraco.cms.businesslogic.ContentType.GetDataType(docType.Alias, prop.Alias))))));
            return string.Format(code,
                                 docType.Text.Replace("\"", "\\\""),
                                 docType.IconUrl,
                                 templates,
                                 childNodes,
                                 CleanName(docType.Alias),
                                 (docType.MasterContentType == 0
                                      ? "DocumentTypeBase"
                                      : CleanName(new DocumentType(docType.MasterContentType).Alias)),
                                 properties,
                                 !string.IsNullOrEmpty(txtDocTypesNamespace.Text)
                                     ? "namespace " + txtDocTypesNamespace.Text
                                     : "",
                                docType.Alias.Replace("\"", "\\\""));
        }
    }
}